package com.nexgo.payment.action.online.upload;

import com.nexgo.common.ByteUtils;
import com.nexgo.data.ChannelType;
import com.nexgo.data.entity.RecvData;
import com.nexgo.data.entity.mapper.EntityDataMapper;
import com.nexgo.data.exception.ResponseException;
import com.nexgo.data.json.JsonRecData;
import com.nexgo.data.json.JsonSendData;
import com.nexgo.data.json.JsonTools;
import com.nexgo.data.json.bean.JsonBody;
import com.nexgo.data.repository.PaymentFactory;
import com.nexgo.domain.interactor.SendAndRecv;
import com.nexgo.domain.repository.PaymentPlatform;
import com.nexgo.payment.constrant.PreferencesConstants;
import com.nexgo.payment.constrant.ResponseState;
import com.nexgo.payment.databases.DataSource;
import com.nexgo.payment.entity.MessageData;
import com.nexgo.payment.entity.TransData;
import com.nexgo.payment.pack.DataBuilder;
import com.nexgo.payment.util.ByteUtil;
import com.nexgo.payment.util.MessageHelper;
import com.nexgo.payment.util.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.reactivex.observers.DisposableObserver;

/**
 * Created by xiaox on 2017/8/16.
 */
public abstract class Upload {
    private static final Logger log = LoggerFactory.getLogger(Upload.class.getSimpleName());
    protected DataSource mRepository;
    private SendAndRecv mComm;
    public static int sUploadCounter = 0;
    private MessageData mMessageData;

    Upload(DataSource repository) {
        this.mRepository = repository;
    }

    /**
     * 组包时调用
     *
     * @return
     */
    abstract TransData buildTransData();

    /**
     * 外部调用开启上送
     *
     * @param listener
     */
    public abstract void sendTrans(OnUploadListener listener);

    /**
     * 接收包检验失败时调用,最后走到onTransComplete()
     *
     * @param responseCode 响应码
     */
    abstract void onTransResponseCheckError(String responseCode);

    /**
     * 接收包检验成功时调用，最后走到onTransComplete()
     *
     * @param messageData
     */
    abstract void onTransResponse(MessageData messageData);

    /**
     * 上送超时时调用，到此结束
     */
    abstract void onTransTimeout();

    /**
     * 网络连接异常时调用，本次上送结束
     */
    abstract void onTransFailed();

    /**
     * 上送成功或不满足上送条件时调用
     */
    abstract void onTransComplete();

    /**
     * 是否超过最大重发次数
     *
     * @return
     */
    public abstract boolean isOverMaxReUploadTimes();

    public static Upload getInstance(NowTask nowTask, DataSource repository) {
        log.debug("当前上送任务[{}]", nowTask);
        Upload upload = null;
        switch (nowTask) {
            case SCRIPT:
                upload = new ScriptNotify(repository);
                break;
            case REVERSAL:
                upload = new Reversal(repository);
                break;
            case MAG_OFFLINE:
                upload = new UploadMagOffline(repository);
                break;
            case ICC_OFFLINE:
                upload = new UploadIccOffline(repository);
                break;
            /*case ES_ONLINE_LAST_BUT_NOT_CURRENT:
                upload = new UploadEsOnlineLastButNotCurrent(repository);
                break;
            case ES_OFFLINE_NEVER_UPLOAD:
                upload = new UploadEsOfflineNever(repository);
                break;
            case ES_NEVER_UPLOAD:
                upload = new UploadEsNever(repository);
                break;
            case ES_UPLOAD_FAILED:
                upload = new UploadEsFailed(repository);
                break;*/
/*            case MAG_BATCH_OFFLINE:
                upload = new BatchUploadMagOffline(repository);
                break;
            case ICC_BATCH_OFFLINE:
                upload = new BatchUploadIccOffline(repository);
                break;
            case MAG_BATCH_ONLINE:
                upload = new BatchUploadMagOnline(repository);
                break;
            case MAG_BATCH_NOTIFY:
                upload = new BatchUploadMagNotify(repository);
                break;
            case ICC_BATCH_NOTIFY:
                upload = new BatchUploadIccNotify(repository);
                break;*/
            case ICC_BATCH_ONLINE:
                upload = new BatchUploadIccOnline(repository);
                break;
            case ICC_BATCH_OFFLINE_DECLINED:
                upload = new BatchUploadIccOfflineDeclined(repository);
                break;
            case ICC_BATCH_ONLINE_ARPC:
                upload = new BatchUploadIccOnlineAprc(repository);
                break;
            default:
                break;
        }
        return upload;
    }

    public enum NowTask {
        /**
         * 脚本通知上送
         */
        SCRIPT,
        /**
         * 冲正上送
         */
        REVERSAL,
        /**
         * 最后一笔非当前交易的联机电子签名
         */
        ES_ONLINE_LAST_BUT_NOT_CURRENT,
        /**
         * 从未上送过的脱机交易的电子签名
         */
        ES_OFFLINE_NEVER_UPLOAD,
        /**
         * 从未上送过的电子签名
         */
        ES_NEVER_UPLOAD,
        /**
         * 上送过，但是上送失败的电子签名
         */
        ES_UPLOAD_FAILED,
        /**
         * 磁条卡脱机交易上送
         */
        MAG_OFFLINE,
        /**
         * IC卡脱机交易上送
         */
        ICC_OFFLINE,
        /**
         * 磁条卡脱机交易批上送
         */
        MAG_BATCH_OFFLINE,
        /**
         * IC卡脱机交易批上送
         */
        ICC_BATCH_OFFLINE,
        /**
         * 磁条卡联机交易批上送
         */
        MAG_BATCH_ONLINE,
        /**
         * 磁条卡通知交易批上送
         */
        MAG_BATCH_NOTIFY,
        /**
         * IC卡通知交易批上送
         */
        ICC_BATCH_NOTIFY,
        /**
         * IC卡联机交易批上送
         */
        ICC_BATCH_ONLINE,
        /**
         * IC卡脱机拒绝交易批上送
         */
        ICC_BATCH_OFFLINE_DECLINED,
        /**
         * IC卡联机交易arpc错批上送
         */
        ICC_BATCH_ONLINE_ARPC,
        /**
         * wzp扫码交易查询
         */
        SCAN_ONLINE_QUERY,
        /**
         * wzp扫码交易撤销
         */
        SCAN_ONLINE_VOID,
        /**
         * wzp扫码交易取消
         */
        SCAN_ONLINE_CACENL,
        /**
         * wzp扫码下单
         */
        SCAN_ONLINE_ORDER,
    }

    /**
     * 上送监听器
     */
    public interface OnUploadListener {
        /**
         * 用于提示当前正在进行什么上送
         *
         * @param tip 提示语
         */
        void onShowTip(String tip);

        /**
         * 网络有问题或是其它不可继续上送的问题导致退出上送
         */
        void onUploadFailed();

        /**
         * 当前任务上送成功
         */
        void onUploadComplete();
    }

    private class TransObserver extends DisposableObserver<Object> {

        @Override
        public void onNext(@io.reactivex.annotations.NonNull Object recvData) {
            MessageData messageData = (MessageData) recvData;
            //判断第一层响应码判断
            if(messageData.getJsonRecData().getRSP_HEAD().getTRAN_SUCCESS().equals("1")){
                mMessageData.setJsonRecData(messageData.getJsonRecData());

                JsonRecData jsonRecData = mMessageData.getJsonRecData();
                JsonSendData jsonSendData = mMessageData.getJsonSendData();

                //第二层响应码判断
                if(jsonRecData.getREQ_BODY().getResponseType().equals("S")){
                    //lr 需要优化
                    onTransResponseCheckError("06");
                    return;
                }else if(jsonRecData.getREQ_BODY().getResponseType().equals("E")){
                    if(jsonRecData.getREQ_BODY().getResponseMessage() != null||jsonRecData.getREQ_BODY().getResponseMsg() != null) {
                        onTransFailed();
                    }
                    return;
                }
                //解密加密Decrypt
                String str = jsonRecData.getREQ_BODY().getDECRYPT();
                log.debug("加密Decrypt = {}",str);
                try {
                    //xhj add
//                    String str1 = UnionApi.getInstance().DecryptData(2, jsonSendData.getREQ_HEAD().getTranscode(),
//                            jsonSendData.getREQ_HEAD().getMcht_id(),jsonSendData.getREQ_HEAD().getTerm_trans_time(), str);
                    String str1 = ByteUtil.CalcByWKey(str,true);
                    log.debug("解密Decrypt = {}",str1);
                    jsonRecData.getREQ_BODY().setDECRYPT("{"+str1+"}");//解密出来的数据前后补{}
                    JsonBody jsonBody = JsonTools.fromJson( jsonRecData.getREQ_BODY().getDECRYPT(),JsonBody.class);
                    byte[] recv8583Data = ByteUtils.commonString2Bcd(jsonBody.get_$8583data());
                    log.debug("接收的8583 = {}", ByteUtils.byteArray2HexString(recv8583Data));
                    //解8583包
                    if(recv8583Data != null) {
                        final EntityDataMapper mapper = new EntityDataMapper();
                        RecvData rData = mapper.rTransform(recv8583Data);
                        mMessageData.setRecvData(rData);
                    }
                } catch (Exception e) {
                    log.debug("处理接收数据失败");
                    transFail();
                    e.printStackTrace();
                }

                if (check(mMessageData)) {
                    onTransResponse(mMessageData);
                } else {
                    log.debug("响应码 {}", mMessageData.getRecvData().getRespCode());
                    onTransResponseCheckError(mMessageData.getRecvData().getRespCode());
                }
            }else{
                // lr 需要优化
                onTransResponseCheckError("06");
            }
        }

        @Override
        public void onError(@io.reactivex.annotations.NonNull Throwable e) {
            e.printStackTrace();
            if (e instanceof ResponseException) {
                log.debug("网络超时");
                transTimeout();
            } else {
                log.debug("网络异常");
                transFail();
            }
        }

        @Override
        public void onComplete() {
            transComplete();
        }
    }

    private void transTimeout() {
        mComm.dispose();
        onTransTimeout();
    }

    private void transComplete() {
        mComm.dispose();
        onTransComplete();
    }

    private void transFail() {
        mComm.dispose();
        onTransFailed();
    }


    void send() {
//        String ip = mRepository.getParamValue(PreferencesConstants.SERVER_IP);
        //wzp add
        String dnsUrl = mRepository.getParamValue(PreferencesConstants.DNS_URL);
        int port = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.SERVER_PORT), "0000"));
        int timeout = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.COMMUNICATION_TIMEOUT), "15"));
        ChannelType type = ChannelType.SOCKET;
        if ("1".equals(mRepository.getParamValue(PreferencesConstants.ENABLE_SSL))) {
            type = ChannelType.HTTPS;
        }
        PaymentPlatform repository = PaymentFactory.getInstance(type, dnsUrl, port, timeout);
        mComm = new SendAndRecv(repository);
        mMessageData = DataBuilder.getInstance(buildTransData(),mRepository).buildJson();
//        mMessageData.setSendData(sendData);
//        mMessageData.setJsonSendData(sSendData);
        mComm.execute(new TransObserver(), mMessageData);
    }

    boolean check(MessageData messageData) {
        int keyIdx = Integer.parseInt(StringUtils.firstNonEmpty(mRepository.getParamValue(PreferencesConstants.MASTER_KEY_INDEX), "0"));
        ResponseState responseState = MessageHelper.checkResponse(messageData.getSendData(), messageData.getRecvData(), keyIdx);
        return ResponseState.SUCCESS.equals(responseState);
    }
}
